/*
 *				Twidere - Twitter client for Android
 * 
 * Copyright (C) 2012 Mariotaku Lee <mariotaku.lee@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.mariotaku.twidere.loader;

import static org.mariotaku.twidere.util.Utils.parseString;

import java.util.ArrayList;
import java.util.List;

import org.mariotaku.twidere.Constants;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.content.AsyncTaskLoader;

public class ExtensionsListLoader extends AsyncTaskLoader<List<ExtensionsListLoader.ExtensionInfo>> implements
		Constants {

	private PackageIntentReceiver mPackageObserver;
	private final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
	private final PackageManager mPackageManager;

	public ExtensionsListLoader(final Context context, final PackageManager pm) {
		super(context);
		mPackageManager = pm;
	}

	@Override
	public List<ExtensionInfo> loadInBackground() {
		final List<ApplicationInfo> apps = mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA);
		final List<ExtensionInfo> extensions = new ArrayList<ExtensionInfo>();
		for (final ApplicationInfo info : apps) {
			final Bundle meta = info.metaData;
			if (meta != null && meta.getBoolean(METADATA_KEY_EXTENSION, false)) {
				extensions.add(new ExtensionInfo(info, mPackageManager));
			}
		}
		return extensions;
	}

	/**
	 * Handles a request to completely reset the Loader.
	 */
	@Override
	protected void onReset() {
		super.onReset();

		// Ensure the loader is stopped
		onStopLoading();

		// Stop monitoring for changes.
		if (mPackageObserver != null) {
			getContext().unregisterReceiver(mPackageObserver);
			mPackageObserver = null;
		}
	}

	/**
	 * Handles a request to start the Loader.
	 */
	@Override
	protected void onStartLoading() {

		// Start watching for changes in the app data.
		if (mPackageObserver == null) {
			mPackageObserver = new PackageIntentReceiver(this);
		}

		// Has something interesting in the configuration changed since we
		// last built the app list?
		final boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());

		if (takeContentChanged() || configChange) {
			// If the data has changed since the last time it was loaded
			// or is not currently available, start a load.
			forceLoad();
		}
	}

	/**
	 * Handles a request to stop the Loader.
	 */
	@Override
	protected void onStopLoading() {
		// Attempt to cancel the current load task if possible.
		cancelLoad();
	}

	public static class ExtensionInfo implements Comparable<ExtensionInfo> {
		public final int permissions;
		public final String label, description;
		public final String pname, settings;
		public final Drawable icon;

		ExtensionInfo(final ApplicationInfo info, final PackageManager pm) {
			final Bundle meta = info.metaData;
			permissions = meta.getInt(METADATA_KEY_PERMISSIONS, PERMISSION_INVALID);
			settings = meta.getString(METADATA_KEY_SETTINGS);
			icon = info.loadIcon(pm);
			pname = info.packageName;
			label = parseString(info.loadLabel(pm), pname);
			description = parseString(info.loadDescription(pm));
		}

		@Override
		public int compareTo(final ExtensionInfo another) {
			return label.compareToIgnoreCase(another.label);
		}

		@Override
		public String toString() {
			return "ExtensionInfo{permission=" + permissions + ", label=" + label + ", description=" + description
					+ ", pname=" + pname + ", settings=" + settings + ", icon=" + icon + "}";
		}
	}

	/**
	 * Helper for determining if the configuration has changed in an interesting
	 * way so we need to rebuild the app list.
	 */
	public static class InterestingConfigChanges {

		final Configuration mLastConfiguration = new Configuration();
		int mLastDensity;

		boolean applyNewConfig(final Resources res) {
			final int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
			final boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
			if (densityChanged
					|| (configChanges & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
				mLastDensity = res.getDisplayMetrics().densityDpi;
				return true;
			}
			return false;
		}
	}

	/**
	 * Helper class to look for interesting changes to the installed apps so
	 * that the loader can be updated.
	 */
	public static class PackageIntentReceiver extends BroadcastReceiver {

		final ExtensionsListLoader mLoader;

		public PackageIntentReceiver(final ExtensionsListLoader loader) {
			mLoader = loader;
			final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
			filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
			filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
			filter.addDataScheme("package");
			mLoader.getContext().registerReceiver(this, filter);
			// Register for events related to sdcard installation.
			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
				final IntentFilter sdFilter = new IntentFilter();
				sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
				sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
				mLoader.getContext().registerReceiver(this, sdFilter);
			}
		}

		@Override
		public void onReceive(final Context context, final Intent intent) {
			// Tell the loader about the change.
			mLoader.onContentChanged();
		}
	}

}
